﻿/*
 * RETRO 2012 - v2.3
 * 
 * PaRADIIT Project
 * https://sites.google.com/site/paradiitproject/
 * 
 * This software is provided under LGPL v.3 license, 
 * which exact definition can be found at the following link:
 * http://www.gnu.org/licenses/lgpl.html
 * 
 * Please, contact us for any offers, remarks, ideas, etc.
 * 
 * Copyright © RFAI, LI Tours, 2011-2012
 * Contacts : rayar@univ-tours.fr
 *            ramel@univ-tours.fr
 * 
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

using Retro.Model.core;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.IO;

using RetroGUI.typography;

namespace RetroGUI.visualisation
{
    /// <summary>
    /// Define Cluster Window
    /// </summary>
    public partial class ClusterWindow : Window
    {
        #region Attributes

        private int index = 0;
        private ObservableCollection<Retro.Model.core.Shape> TwelveShapes = new ObservableCollection<Retro.Model.core.Shape>();

        private ModelMetaData metadata = new ModelMetaData();
        private AddModelDataWindow amdw = new AddModelDataWindow();
        private Image current_image = new Image();
        private Retro.Model.core.Shape current_shape = new Retro.Model.core.Shape();

        private ShapeContextWindow scw = new ShapeContextWindow();

        private Cluster _CurrentCluster;
        /// <summary>
        /// Current selected Cluster
        /// </summary>
        public Cluster CurrentCluster
        {
            get { return _CurrentCluster; }
            set { _CurrentCluster = value; }

        }

        private List<Retro.Model.core.Shape> _Shapes;
        /// <summary>
        /// Shape list
        /// </summary>
        public List<Retro.Model.core.Shape> Shapes
        {
            get { return _Shapes; }
            set { _Shapes = value; }

        }

        private bool _HasBeenClosed;
        /// <summary>
        /// To avoid multiple instince of ClusterWindows ans handle it's closing
        /// </summary>
        public bool HasBeenClosed
        { 
            get { return _HasBeenClosed; }
            set { _HasBeenClosed = value; }
        }

        #endregion


        /// <summary>
        /// Constructor
        /// </summary>
        public ClusterWindow()
        {
            InitializeComponent();
            this.HasBeenClosed = false;
        }


        /// <summary>
        /// Update the view regarding the current cluster that is considered
        /// </summary>
        public void UpdateView()
        {
            this.label_clusterno.Content = this.CurrentCluster.ClusterId;
            this.label_nbshapes.Content = this.CurrentCluster.NbShapes;
            this.textBox_label.Text = (this.CurrentCluster.LabelList.Count != 0) ? this.CurrentCluster.LabelList.ElementAt(0) : "[" + this.CurrentCluster.ClusterId + "]";
            this.textBox_label.Focus();
            this.textBox_label.SelectAll();

            // Update representative thumbnail
            BitmapImage src = new BitmapImage();
            src.BeginInit();
            if (File.Exists(this.CurrentCluster.RepresentativePath))
                src.UriSource = new Uri(this.CurrentCluster.RepresentativePath, UriKind.Absolute);
            else
                src.UriSource = new Uri("/resources/LogoLI.png", UriKind.Relative);
            src.EndInit();

            this.RepresentativeImage.Source = src;
            this.RepresentativeImage.Stretch = Stretch.Uniform;

            
            this.Shapes = this.CurrentCluster.ShapesList;
            this.Shape_listBox.ItemsSource = TwelveShapes;

            index = 0;
            UpdateBinding();
            
        }


        /// <summary>
        /// Update the 12 shapes of the cluster that are displayed
        /// </summary>
        private void UpdateBinding()
        {
            TwelveShapes.Clear();
            for (int i = index; i < Math.Min(Shapes.Count, index + 12); i++)
            {
                TwelveShapes.Add(Shapes.ElementAt(i));
            }
        }


        /// <summary>
        /// Handler for TextBox label change
        /// </summary>
        private void ClusterWindow_TextBox_Label_TextChange(object sender, TextChangedEventArgs e)
        {
            if (this.textBox_label.Text != null)
            {
                if (this.CurrentCluster.LabelList.Count > 0)
                {
                    if (this.textBox_label.Text.CompareTo(this.CurrentCluster.LabelList[0]) == 0)
                    {
                        // Don't add a new manual label if it already exist!
                    }
                    else
                    {
                        this.CurrentCluster.AddNewLabel("MANUAL", this.textBox_label.Text, 1.0);
                        this.CurrentCluster.IsLabelized = true;
                    }
                }
                else 
                {
                    if ((this.textBox_label.Text.StartsWith("[")) && (this.textBox_label.Text.EndsWith("]")))
                    {
                        this.CurrentCluster.AddNewLabel("DEFAULT", this.textBox_label.Text, 0.0);
                    }
                    else
                    {
                        this.CurrentCluster.AddNewLabel("MANUAL", this.textBox_label.Text, 1.0);
                        this.CurrentCluster.IsLabelized = true;
                    }
                }

            }
        }


        /// <summary>
        /// Handler for Previous Button Click
        /// </summary>
        private void PreviousShapes_Click(object sender, System.EventArgs e)
        {
            index = (index >= 12) ? index - 12 : 0;
            UpdateBinding();
        }


        /// <summary>
        /// Handler for Next Button Click
        /// </summary>
        private void NextShapes_Click(object sender, System.EventArgs e)
        {
            index = (index < this.Shapes.Count - 12) ? index + 12 : index;
            UpdateBinding();
        }

        /// <summary>
        /// Handler for a right clik on a cluster element
        /// Display ContextMenu of an EoC
        /// </summary>
        private void Cluster_Element_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            // Element must be selected with a left click before accessing the ContextMenu
            if (this.Shape_listBox.SelectedIndex != -1)
            {
                // Enable ContextMenu to the selected cluster element 
                Image current_image = sender as Image;
                current_image.ContextMenu = (ContextMenu)this.LayoutRoot.FindResource("ClusterElementContextMenu");

                // Get selected image and associated shape
                this.current_shape = (Retro.Model.core.Shape)this.Shape_listBox.SelectedItem;
                this.current_image = sender as Image;
            }
        }

        
        /// <summary>
        /// Handler for ContextMenu/Save as FontModel MenuItem Click
        /// Display the ModelDataWindow
        /// </summary>
        private void SaveAsFontModel_Click(object sender, RoutedEventArgs e)
        {
            // Create AddModelDataWidows if needed
            if (this.amdw.HasBeenClosed)
                this.amdw = new AddModelDataWindow();

            this.amdw.SetProvider();
            ImageSource source = this.current_image.Source;
            this.amdw.SetImage(source, "", "" + this.current_shape.Width, "" + this.current_shape.Height, "" + this.current_shape.X, "" + this.current_shape.Y);
            this.amdw.ColorizeBlob = false;
            this.amdw.Owner = this;
            this.amdw.ShowDialog();

            source = null;

        }

        /// <summary>
        /// Handler for ContextMenu/Display Shape Context MenuItem Click
        /// Display Context of the selected Shape
        /// </summary>
        private void DisplayShapeContext_Click(object sender, RoutedEventArgs e)
        {
            // Create ShapeContextWindow if needed
            if (this.scw.HasBeenClosed)
                this.scw = new ShapeContextWindow();

            // Set image
            BitmapSource bitmapSource = GetShapeContextImage(this.current_shape);
            this.scw.SetImage(bitmapSource);

            // Show window
            this.scw.Owner = this;
            this.scw.Show();

        }

        /// <summary>
        /// TODO : Copy of ManualTranscription.GetShapeContextImage
        /// Factorize this code!
        /// Get shape context image 
        /// </summary>
        private BitmapSource GetShapeContextImage(Retro.Model.core.Shape shape)
        {
            // Get the image source
            String imagepath = System.IO.Path.GetDirectoryName(shape.ShapePath);
            imagepath = imagepath.Replace("alto\\images_text\\", "images\\");
            imagepath = imagepath + ".png";

            if (File.Exists(imagepath))
            {
                // Open the image
                System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(imagepath);

                // Convert to color image if grayscale
                if (bitmap.PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
                {
                    AForge.Imaging.Filters.GrayscaleToRGB filter = new AForge.Imaging.Filters.GrayscaleToRGB();
                    bitmap = filter.Apply(bitmap);
                }

                // Get the RoI of the image source corresponding to the context shape
                System.Drawing.Rectangle roi = new System.Drawing.Rectangle(shape.X - 135, shape.Y - 35, shape.Width + 270, shape.Height + 70);

                // Crop the image
                AForge.Imaging.Filters.Crop filter_ = new AForge.Imaging.Filters.Crop(roi);
                System.Drawing.Bitmap croppedBitmap = filter_.Apply(bitmap);

                // Free the source bitmap
                bitmap.Dispose();

                // Lock bits of the cropped image
                System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, croppedBitmap.Width, croppedBitmap.Height);
                System.Drawing.Imaging.BitmapData bitmapData =
                    croppedBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
                    croppedBitmap.PixelFormat);

                // Draw the bounding box of the shape
                // Workaround for a thick rectangle: draw 2 rectangles ...
                System.Drawing.Rectangle shapeBB = new System.Drawing.Rectangle(shape.X - roi.X, shape.Y - roi.Y, shape.Width, shape.Height);
                AForge.Imaging.Drawing.Rectangle(bitmapData, new System.Drawing.Rectangle(shapeBB.X - 1, shapeBB.Y - 1, shapeBB.Width + 2, shapeBB.Height + 2), System.Drawing.Color.Red);
                AForge.Imaging.Drawing.Rectangle(bitmapData, shapeBB, System.Drawing.Color.Red);

                // Unlock bits
                croppedBitmap.UnlockBits(bitmapData);

                // Display the image
                BitmapSource bitmapSource =
                    System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                        croppedBitmap.GetHbitmap(),
                        IntPtr.Zero,
                        Int32Rect.Empty,
                        System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

                // Free the cropped bitmap
                croppedBitmap.Dispose();

                return bitmapSource;
            }
            else
            {
                return null;
            }
        }





        /// <summary>
        /// Override Close event to set a flag in order manage the ClusterWindow Lifetime
        /// </summary>
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
        {
            base.OnClosing(e);
            this.HasBeenClosed = true;
        }
        
    }
}
